//------------------------------------------------------------
//  Filename: camera_driver.v
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2016-10-08 10:54
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc. 					      
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module VIDEO_DRIVER ( 
    input  wire        clk_100mhz,
    input  wire        resetn    ,
    //camera input
    input  wire        camera_video_en,  
    input  wire        camera_pclk,
    input  wire        camera_resetn,
    input  wire [7:0]  camera_yuv422, 
    input  wire        camera_h_sync,  // HREF --- function as data_valid 
    input  wire        camera_v_sync,  // VSYN --- fram sync ,plority high
    output reg  [31:0] camera_param,
    output reg  [31:0] camera_error,
    output reg  [7:0]  camera_bright,

    // User ports fifo
    output wire        sensor_wr_en,
    output wire [32:0] sensor_din
);
//-------------------------------------------------------- 
reg[2:0]  h_sync;
reg[2:0]  v_sync;
reg[23:0] p_data;
reg       d_last;
//--------------------------------------------------------
wire     clk = clk_100mhz;
wire     rst = ~(resetn&camera_resetn);
//--------------------------------------------------------
wire     s0_axis_aclk    = camera_pclk;
wire     s0_axis_aresetn = camera_resetn&resetn;
reg      s0_axis_tvalid  ;
wire     s0_axis_tready  ;
reg[7:0] s0_axis_tdata   ;
reg      s0_axis_tlast   ;
wire     s0_sync_full    ;

wire     m0_axis_aclk    = clk_100mhz;
wire     m0_axis_aresetn = resetn;
wire     m0_axis_tvalid  ;
reg      m0_axis_tready  ;
wire[7:0]m0_axis_tdata   ;
wire     m0_axis_tlast   ;
wire     m0_sync_empty   ;

reg[7:0] p0_axis_tdata   ;
reg      p0_axis_tlast   ;
reg      p0_axis_tvalid  ;

//wire     sync_last   = m0_axis_tvalid&&m0_axis_tready&&m0_axis_tlast;
//wire     sync_data_v = m0_axis_tvalid&&m0_axis_tready;
//wire[7:0]sync_tdata  = m0_axis_tdata;
wire[7:0]sync_tdata  = p0_axis_tdata;
wire     sync_last   = p0_axis_tlast&p0_axis_tvalid;
wire     sync_data_v = p0_axis_tvalid;
//--------------------------------------------------------
always@(posedge camera_pclk ) h_sync <= {h_sync[1:0],camera_h_sync};
always@(posedge camera_pclk ) v_sync <= {v_sync[1:0],camera_v_sync};
always@(posedge camera_pclk ) p_data <= {p_data[15:0],camera_yuv422};
always@(posedge camera_pclk ) d_last <= (v_sync[1:0] == 2'b01)?1'b1:1'b0; 
//-------------------------------------------------------- 
always @(posedge camera_pclk,posedge rst) begin
    if(rst)begin 
        s0_axis_tdata  <= 8'b0;    
    end 
    else begin 
        s0_axis_tdata  <= p_data[15:8];    
    end 
end 
//--------------------------------------------------------
always @(posedge camera_pclk,posedge rst) begin
    if(rst)begin 
        s0_axis_tlast <= 1'b0;   
    end 
    else begin 
        s0_axis_tlast <= d_last;   
    end 
end 
//--------------------------------------------------------
always @(posedge camera_pclk,posedge rst) begin
    if(rst)begin 
        s0_axis_tvalid <= 1'b0;    
    end 
    else begin 
        s0_axis_tvalid <= (h_sync[1]|d_last);    
    end 
end 
//--------------------------------------------------------
reg[7:0]  s0_sync_addr;
reg[6:0]  s0_shift_cnt;
reg[1:0]  s0_shift_addr;
reg[6:0]  s0_shift_v;
//--------------------------------------------------------
always @(posedge camera_pclk,posedge rst) begin
    if(rst)begin 
        s0_sync_addr <= 8'h0;  
        s0_shift_cnt   <= 7'b0;
        s0_shift_addr<= 2'b0;
        s0_shift_v   <= 1'b0;
    end 
    else if(s0_axis_tvalid)begin 
        if(s0_axis_tlast) begin
            s0_sync_addr[7:6] <= s0_sync_addr[7:6] + 2'h1;    
            s0_sync_addr[5:0] <= 6'b0;    
            s0_shift_cnt      <= s0_sync_addr[5:0] + 7'h1; 
            s0_shift_addr     <= s0_sync_addr[7:6];
            s0_shift_v        <= 1'b1;
        end
        else begin
            s0_sync_addr <= s0_sync_addr + 8'b1; 
            if(s0_sync_addr[5:0] == 6'h3f)begin
                s0_shift_cnt <= 7'h40; 
                s0_shift_addr<= s0_sync_addr[7:6];
                s0_shift_v   <= 1'b1;
            end
            else begin
                s0_shift_v   <= 1'b0;
            end
        end
    end 
    else begin
        s0_shift_v <= 1'b0;
    end
end 
//--------------------------------------------------------
reg[8:0] s0_shift_cmd;
reg      s0_shift_cmd_v;
//--------------------------------------------------------
always @(posedge camera_pclk ) s0_shift_cmd   <= {s0_shift_addr,s0_shift_cnt};
always @(posedge camera_pclk ) s0_shift_cmd_v <= s0_shift_v;
//--------------------------------------------------------
reg [31:0] c_counter;
//--------------------------------------------------------
always @(posedge camera_pclk,posedge rst) begin
    if(rst)begin 
        c_counter <= 0;
    end 
    else if(s0_axis_tvalid)begin
        c_counter <= (s0_axis_tlast)?0:(c_counter + 1);
    end
end
//--------------------------------------------------------
reg [23:0] e_counter;
//--------------------------------------------------------
always @(posedge camera_pclk,posedge rst) begin
    if(rst)begin 
        e_counter <= 24'b0;
    end 
    else if(s0_axis_tvalid&&s0_axis_tlast)begin
        e_counter <= c_counter[24:1]; 
    end
end
//********************************************************
//--
//--  CLK 100M domain
//--
//********************************************************
reg [31:0] d_counter;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        d_counter <= 0;
    end 
    else if(sync_data_v)begin
        d_counter <= (sync_last)?0:(d_counter + 1);
    end
end
//--------------------------------------------------------
reg [7:0] f_counter;
reg[31:0] second_cntr;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        second_cntr <= 32'b0;  
        f_counter   <= 8'b0;  
    end 
    else begin 
        second_cntr <= (second_cntr < (1000_000_00 -1))?(second_cntr + 1):0;
        if(second_cntr == (1000_000_00 -1)) begin
            f_counter   <= 8'b0;  
        end
        else if(sync_last)begin
            f_counter   <= f_counter + 1;  
        end
    end 
end 
//--------------------------------------------------------
reg[7:0] frm_rate;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        frm_rate <= 8'b0;    
    end 
    else if(second_cntr == (1000_000_00 -1)) begin 
        frm_rate <= f_counter;    
    end 
end 
//--------------------------------------------------------
reg[31:0] rgb_raw_data;
reg       rgb_raw_v;
reg       rgb_raw_last;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin
        rgb_raw_data <= 32'b0;
        rgb_raw_v    <= 1'b0;
    end
    else if(sync_data_v&&(d_counter[0] == 0))begin
        rgb_raw_data <= {8'b0,sync_tdata,sync_tdata,sync_tdata}; // fixed data ,Y to GRAY only,or color convert function
        rgb_raw_v    <= 1'b1;
    end
    else begin
        rgb_raw_data <= rgb_raw_data;
        rgb_raw_v    <= 1'b0;
    end
end
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        rgb_raw_last <= 1'b0;
    end 
    else if(sync_last) begin 
        rgb_raw_last <= 1'b1;
    end 
    else begin
        rgb_raw_last <= 1'b0;
    end
end 
//--------------------------------------------------------
reg[31:0]   frm_bright_sum ;
reg[31:0]   frm_data_cntr  ;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        frm_bright_sum <= 32'b0;        
        frm_data_cntr  <= 32'b0;        
    end 
    else if(rgb_raw_v&rgb_raw_last)begin 
        frm_bright_sum <= 32'b0;        
        frm_data_cntr  <= 32'b0;        
    end    
    else if(rgb_raw_v&&(frm_data_cntr < 1024))begin  
        frm_bright_sum <= frm_bright_sum + rgb_raw_data[7:0];        
        frm_data_cntr  <= frm_data_cntr + 32'b1;        
    end 
end 
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        camera_bright <= 8'b0;
    end 
    else if(rgb_raw_v&rgb_raw_last)begin 
        camera_bright <= frm_bright_sum[17:10];
    end
end 
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        camera_param <= 0;
    end 
    else if(sync_last) begin
        camera_param <= {frm_rate,d_counter[24:1]};
    end
end
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        camera_error[15:0] <= 0;
    end 
    else if(sync_last&&(d_counter[24:1] != 307200)) begin
        camera_error[15:0] <= camera_error[15:0] + 16'h1;
    end
end
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        camera_error[31:16] <= 0;
    end 
    else if(s0_axis_tvalid&&s0_axis_tlast&&(e_counter != 307200)) begin
        camera_error[31:16] <= camera_error[31:16] + 16'h1;
    end
end
//--------------------------------------------------------
assign sensor_din   = {rgb_raw_last,rgb_raw_data};
assign sensor_wr_en = {rgb_raw_last|rgb_raw_v};
//--------------------------------------------------------
wire[8:0] s0_sync_data = {s0_axis_tlast,s0_axis_tdata};
wire[8:0] m0_sync_data;
//--------------------------------------------------------
reg[3:0] s0_shift_cmd_v_ffx;
//--------------------------------------------------------
always @(posedge clk) s0_shift_cmd_v_ffx <= {s0_shift_cmd_v_ffx[2:0],s0_shift_cmd_v}; // high bit AB shift
//--------------------------------------------------------
reg      new_data_income;
reg[1:0] new_data_rd_ptr;
reg[6:0] new_data_rd_cnt;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        new_data_income <= 1'b0;  
        new_data_rd_ptr <= 2'b0;   
        new_data_rd_cnt <= 7'h0; 
    end 
    else if(s0_shift_cmd_v_ffx[3:2] == 2'b01) begin 
        new_data_income <= 1'b1; // shift indication
        new_data_rd_ptr <= s0_shift_cmd[8:7];   
        new_data_rd_cnt <= s0_shift_cmd[6:0];
    end 
    else begin
        new_data_income <= 1'b0;        
    end
end 
//--------------------------------------------------------
reg[7:0] m0_sync_addr;
reg[6:0] ab_sync_addr;
reg      ab_data_valid;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        m0_sync_addr  <= 8'h0; 
        ab_sync_addr  <= 7'h7f;        
        ab_data_valid <= 1'b0;   
    end 
    else if(new_data_income) begin 
        m0_sync_addr  <= {new_data_rd_ptr,6'h0};    
        ab_sync_addr  <= 7'b0;
        ab_data_valid <= 1'b1;   
    end 
    else if((ab_sync_addr + 7'b1) < new_data_rd_cnt)begin
        m0_sync_addr  <= m0_sync_addr + 8'b1;    
        ab_sync_addr  <= ab_sync_addr + 7'b1;    
        ab_data_valid <= 1'b1;   
    end
    else begin
        ab_data_valid <= 1'b0;   
    end
end 
//--------------------------------------------------------
reg[1:0] m0_axis_tvalid_ffx;
always @(posedge clk) m0_axis_tvalid_ffx <= {m0_axis_tvalid_ffx[0],ab_data_valid};
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        p0_axis_tdata   <= 8'b0;
        p0_axis_tlast   <= 1'b0;
        p0_axis_tvalid  <= 1'b0;            
    end 
    else if(m0_axis_tvalid_ffx[1])begin 
        p0_axis_tdata   <= m0_sync_data[7:0];
        p0_axis_tlast   <= m0_sync_data[8];
        p0_axis_tvalid  <= 1'b1;                  
    end 
    else begin
        p0_axis_tvalid  <= 1'b0;                  
    end
end 
//--------------------------------------------------------
sync_data_buffer_9x256  sync_data_buffer_inst0 (
    .clka   ( s0_axis_aclk    ) ,
    .ena    ( camera_video_en ) ,
    .addra  ( s0_sync_addr    ) ,
    .dina   ( s0_sync_data    ) ,
    .wea    ( s0_axis_tvalid  ) ,
   
    .clkb   ( m0_axis_aclk    ) ,
    .enb    ( camera_video_en ) ,
    .web    ( 1'b0            ) ,
    .addrb  ( m0_sync_addr    ) ,
    .doutb  ( m0_sync_data    )
);

endmodule

